package io.kiki.sba.registry.common.model.store;


import io.kiki.sba.registry.cache.Sizer;
import io.kiki.sba.registry.compress.CompressDatumKey;
import io.kiki.sba.registry.util.CollectionUtils;
import io.kiki.sba.registry.util.StringUtils;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Setter
@Getter
public final class SubDatum implements Serializable, Sizer {
    private static final long serialVersionUID = 1L;
    @Getter
    private final String dataCenter;
    @Getter
    private final String dataInfoId;
    @Getter
    private final String dataId;

    @Getter
    private final String instanceId;

    @Getter
    private final String group;


    private final List<SubPublisher> subPublisherList;

    @Getter
    private final long version;

    @Getter
    private final List<Long> recentVersionList;

    @Getter
    private final ZipSubPublisherList zipSubPublisherList;

    private final int byteSize;
    private final int dataBoxSizeCache;

    private SubDatum(String dataCenter, String dataInfoId, String dataId, String instanceId, String group, List<SubPublisher> subPublisherList, long version, List<Long> recentVersionList, ZipSubPublisherList zipSubPublisherList) {
        this.dataCenter = dataCenter;
        //        only one must be not null between publishers and zipPublishers
        this.dataInfoId = dataInfoId;
        this.dataId = dataId;
        this.instanceId = instanceId;
        this.group = group;
        this.version = version;
        this.recentVersionList = recentVersionList;
        if (subPublisherList != null) {
            this.subPublisherList = new ArrayList<>(subPublisherList);
        } else {
            this.subPublisherList = null;
        }
        this.zipSubPublisherList = zipSubPublisherList;

        this.byteSize = calcSize();
        this.dataBoxSizeCache = getByteSize2();
    }

    public static SubDatum emptyOf(String dataCenter, String dataInfoId, String dataId, String instanceId, String group, long version) {//
        return new SubDatum(dataCenter, dataInfoId, dataId, instanceId, group, Collections.emptyList(), version, Collections.emptyList(), null);
    }

    public static SubDatum normalOf(String dataCenter, String dataInfoId, String dataId, String instanceId, String group, long version, List<SubPublisher> subPublisherList, List<Long> recentVersions) {//
        return new SubDatum(dataCenter, dataInfoId, dataId, instanceId, group, subPublisherList, version, recentVersions, null);
    }

    public static SubDatum zipOf(String dataCenter, String dataInfoId, String dataId, String instanceId, String group, long version, List<Long> recentVersions, ZipSubPublisherList zipSubPublisherList) {//
        return new SubDatum(dataCenter, dataInfoId, dataId, instanceId, group, null, version, recentVersions, zipSubPublisherList);
    }

    public static SubDatum intern(SubDatum subDatum) {
        if (subDatum == null) {
            return null;
        }

        final String dataInfoId = StringInterner.intern(subDatum.dataInfoId);
        final String dataCenter = StringInterner.intern(subDatum.dataCenter);

        final String dataId = StringInterner.intern(subDatum.dataId);
        final String instanceId = StringInterner.intern(subDatum.instanceId);
        final String group = StringInterner.intern(subDatum.group);

        List<SubPublisher> subPublisherList = null;
        if (subDatum.subPublisherList != null) {
            subPublisherList = new ArrayList<>(subDatum.getUnzippedSubPublisherList().size());
            for (SubPublisher subPublisher : subDatum.getUnzippedSubPublisherList()) {
                final String cell = StringInterner.intern(subPublisher.getCell());
                subPublisherList.add(new SubPublisher(subPublisher.getRegisterId(), cell, subPublisher.getServerDataList(), subPublisher.getClientId(), subPublisher.getVersion(), subPublisher.getSrcAddressString(), subPublisher.getRegisterTimestamp(), subPublisher.getPublishSource()));
            }
        }
        return new SubDatum(dataCenter, dataInfoId, dataId, instanceId, group, subPublisherList, subDatum.version, subDatum.recentVersionList, subDatum.zipSubPublisherList);
    }

    public int getSubPublisherListSize() {
        if (zipSubPublisherList != null) {
            return zipSubPublisherList.getPubNum();
        }
        return subPublisherList.size();
    }

    public List<SubPublisher> getUnzippedSubPublisherList() {
        mustUnzipped();
        return subPublisherList;
    }

    private int getByteSize2() {
        if (zipSubPublisherList != null) {
            return zipSubPublisherList.getOriginSize();
        }
        int bytes = 0;
        bytes += CollectionUtils.fuzzyTotalSize(subPublisherList, SubPublisher::getByteSize);
        return bytes;
    }


    private int calcSize() {
        int size = 50;
        size += StringUtils.sizeof(dataInfoId) + StringUtils.sizeof(dataCenter) + StringUtils.sizeof(dataId) + StringUtils.sizeof(instanceId);
        size += CollectionUtils.fuzzyTotalSize(subPublisherList, SubPublisher::size);
        if (zipSubPublisherList != null) {
            size += zipSubPublisherList.size();
        }
        return size;
    }

    @Override
    public int size() {
        return byteSize;
    }

    public CompressDatumKey compressKey(String encoding) {
        return new CompressDatumKey(encoding, dataInfoId, dataCenter, version, getSubPublisherListSize());
    }

    public void mustUnzipped() {
        Assert.notNull(subPublisherList, "publishers must be not null");
    }
}
